package belf.migrate.engine.ddl.view.mysql;

import belf.migrate.api.exception.APIErrorCode;
import belf.migrate.api.exception.CatalogException;
import belf.migrate.api.model.QualifiersModel;
import belf.migrate.api.table.schema.Catalog;
import belf.migrate.api.util.QuoteType;
import belf.migrate.core.jdbc.FunctionModel;
import belf.migrate.engine.ddl.BaseDDLConvert;
import belf.migrate.engine.ddl.helper.mysql.MySqlAntlrBuilder;
import belf.migrate.plugin.mysql.antlr.MySqlLexer;
import belf.migrate.plugin.mysql.antlr.MySqlParser;
import belf.migrate.plugin.mysql.antlr.MySqlParserBaseListener;
import belf.migrate.plugin.mysql.antlr.MySqlParserBaseVisitor;
import com.github.vertical_blank.sqlformatter.SqlFormatter;
import com.github.vertical_blank.sqlformatter.languages.Dialect;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;

/**
 * 将MySQL创建视图的DDL转化为达梦的DDL
 */
public class MySql2DMViewConvert extends BaseDDLConvert {

    /** MySql函数到达梦函数的映射 */
    private Map<String, FunctionModel> mappings;

    public MySql2DMViewConvert(String objectName, String sql, Catalog sourceCatalog, Catalog sinkCatalog,
                               String schema, QualifiersModel qualifiersModel) {
        super(objectName, sql, sourceCatalog, sinkCatalog, schema, qualifiersModel);
    }

    @Override
    public String convert() {
        CharStream input = CharStreams.fromString(sql);

        // 创建词法分析器和解析器
        MySqlLexer lexer = new MySqlLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        MySqlParser parser = new MySqlParser(tokens);

        // 从解析器中获取起始规则并进行解析
        MySqlParser.RootContext tree = parser.root();
        MySqlViewVisitor viewVisitor = new MySqlViewVisitor(this);
        viewVisitor.visit(tree);

        return viewVisitor.toDMViewDDL();
    }

    private class MySqlViewVisitor extends MySqlParserBaseVisitor<String> {
        private String viewName;

        private MySqlParser.QuerySpecificationContext queryRoot;

        private BaseDDLConvert ddlConvert;

        public MySqlViewVisitor(BaseDDLConvert ddlConvert) {
            this.ddlConvert = ddlConvert;
        }

        /**
         * 获取视图名称，并去掉名称两边的反引号;找到QuerySpecification节点，后面通过listener模式去遍历生成select部分
         *
         * @param ctx
         * @return
         */
        @Override
        public String visitCreateView(MySqlParser.CreateViewContext ctx) {
            if (ctx.fullId().uid().size() == 2) {
                viewName = ctx.fullId().uid().get(1).getText();
            } else {
                viewName = ctx.fullId().uid().get(0).getText();
            }
            viewName = StringUtils.replace(viewName, "`", "");
            queryRoot = (MySqlParser.QuerySpecificationContext) ctx.selectStatement().getChild(0);
            return visitChildren(ctx);
        }

        public String toDMViewDDL() {
            StringBuilder sb = new StringBuilder();
            sb.append("CREATE OR REPLACE VIEW "); //注意：FORCE参数，可以无视物理表是否存在，直接创造视图
            if (schema != null) {
                sb.append(qualifiersModel.getLeftQuote() + schema + qualifiersModel.getRightQuote() + ".");
            }
            sb.append(qualifiersModel.getLeftQuote() + viewName + qualifiersModel.getRightQuote());
            sb.append(" AS ");

            MySqlAntlrBuilder builder = new MySqlAntlrBuilder(ddlConvert, sb, QuoteType.DOUBLE_QUOTE.getValue(), schema);
            MySqlViewListener listener = new MySqlViewListener(builder);
            ParseTreeWalker walker = new ParseTreeWalker();
            walker.walk(listener, queryRoot);

            return SqlFormatter.of(Dialect.PlSql).format(sb.toString()); //达梦数据库的语法兼容Oracle
//            return sb.toString();
        }
    }

    private class MySqlViewListener extends MySqlParserBaseListener {
        private MySqlAntlrBuilder builder;

        public MySqlViewListener(MySqlAntlrBuilder builder) {
            this.builder = builder;
        }

        /**
         * 仅用于遍历访问节点"“"selectStatement"-->"querySpecification"的叶子节点
         *
         * @param node
         */
        @Override
        public void visitTerminal(TerminalNode node) {
            String text = node.getText().trim();
            if (text.equalsIgnoreCase("CONCAT") && hasAncestor("FunctionNameBaseContext", node)) {
                addWarning(new CatalogException(APIErrorCode.CONVERT_VIEW_ERROR, "CONCAT连接函数在达梦中需要两个及以上的参数！"));
            }
            if (isPartOfGroupConcat(node)) { // 处理聚集函数group_concat下面的节点
                addWarning(new CatalogException(APIErrorCode.CONVERT_VIEW_ERROR, "mysql分组函数group_concat在达梦中无直接对应，需要手动调整！"));
                //TODO: 需要找更多案例，看看是否还有其他非group_concat的聚集函数
                if (text.equalsIgnoreCase("group_concat")) {
                    builder.append(" wm_concat");

                } else if (node.getParent() instanceof MySqlParser.AggregateWindowedFunctionContext) {
                    // 去掉seperator参数，因为达梦的wm_concat不支持
                    if (text.equalsIgnoreCase("DISTINCT")) {
                        builder.append(text + " ");
                    } else if (text.equals("(") || text.equals(")")) {
                        builder.append(text);
                    }

                } else if (BaseDDLConvert.hasAncestor("functionNameBase", node)) {
                    builder.append(text);
                } else if (hasAncestorOrderByExpression(node)) { //去掉order by，因为达梦的wm_concat不支持
                    //直接忽略
                } else if (MySqlAntlrBuilder.TRIMLEFT_SYMBOLS.contains(text)) {
                    if (builder.charAt(builder.length() - 1) == ' ') {
                        // 删除最后一个字符
                        builder.deleteCharAt(builder.length() - 1);
                    }
                    builder.append(text);
                } else if (MySqlAntlrBuilder.PREFIX_SYMBOLS.contains(text)) {
                    builder.append(text);
                } else if (MySqlAntlrBuilder.SUFFIX_SYMBOLS.contains(text)) {
                    if (builder.charAt(builder.length() - 1) == ' ') {
                        // 删除最后一个字符
                        builder.deleteCharAt(builder.length() - 1);
                    }
                    builder.append(text);
                } else if (BaseDDLConvert.SQL_KEYWORDS.contains(text.toUpperCase())){
                    builder.appendTrimLeft(text);
                } else if (text.equals("(") || text.equals(")")){
                    while (builder.charAt(builder.length() - 1) == ' ') {
                        builder.deleteCharAt(builder.length() - 1);
                    }
                    builder.append(text);
                } else {
                    builder.output(node);
                }

            } else {
                builder.output(node);
            }
        }

        /**
         * 判断当前叶子节点是否有祖先节点AggregateWindowedFunction，有则返回AggregateWindowedFunction节点；无则返回null
         *
         * @param node
         * @return
         */
        private MySqlParser.AggregateWindowedFunctionContext getAggregateWindowedFunctionNode(TerminalNode node) {
            ParseTree parent = node.getParent();
            while (parent != null) {
                if (parent instanceof MySqlParser.AggregateWindowedFunctionContext) {
                    return (MySqlParser.AggregateWindowedFunctionContext) parent;
                }
                parent = parent.getParent();
            }
            return null;
        }

        /**
         * 叶子节点是否是MySql的聚集函数group_concat的组成部分
         * @param node
         * @return
         */
        private boolean isPartOfGroupConcat(TerminalNode node) {
            MySqlParser.AggregateWindowedFunctionContext ancestor = getAggregateWindowedFunctionNode(node);
            if (null != ancestor) {
                if (ancestor.getChild(0).getText().equalsIgnoreCase("group_concat")) {
                    return true;
                }
            }
            return false;
        }

        /**
         * 判断当前叶子节点的祖先是否有OrderByExpression节点
         *
         * @param node
         * @return
         */
        private boolean hasAncestorOrderByExpression(TerminalNode node) {
            ParseTree parent = node.getParent();
            while (parent != null) {
                if (parent instanceof MySqlParser.OrderByExpressionContext) {
                    return true;
                }
                parent = parent.getParent();
            }
            return false;
        }
    }

}
